﻿using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using Microsoft.Phone.Controls;
using Telerik.Windows.Controls;
using Windows.Networking.Proximity;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;

namespace JustinAngel.MindwaveSensor.WP8.TestApp
{
    public partial class Standalone : PhoneApplicationPage
    {
        // Constructor
        public Standalone()
        {
            InitializeComponent();

            // Sample code to localize the ApplicationBar
            //BuildLocalizedApplicationBar();\

        }

        private StreamSocket socket;
        private async void ConnectToMindset(object sender, RoutedEventArgs e)
        {
            PeerFinder.AlternateIdentities["Bluetooth:Paired"] = "";

            VisualStateManager.GoToState(this, this.Connecting.Name, true);

            var peers = await PeerFinder.FindAllPeersAsync();

            if (!peers.Any(p => p.DisplayName.Contains("MindWave")))
            {
                Print("MindWave not found. Is bluetooth on? Is MindWave paired?");
                VisualStateManager.GoToState(this, this.NotConnected.Name, true);
            }
            else
            {
                PeerInformation spheroPeer = peers.First(p => p.DisplayName.Contains("MindWave"));

                socket = new StreamSocket();

                await socket.ConnectAsync(spheroPeer.HostName, "1");

                Print("Connected to " + peers[0].DisplayName + " bluetooth service.");
                VisualStateManager.GoToState(this, this.Connecting.Name, true);

                ListenToResult();
            }
        }

        private async void ListenToResult()
        {
            while (true)
            {
                var resultArray = await GetNextBuffer();
                //Debug.WriteLine(string.Join(",", resultArray.Select(b => b.ToString())));

                int? indexOfUsefulDataHeader = GetUsefulDataHeaderIndex(resultArray);
                if (indexOfUsefulDataHeader.HasValue == false)
                {
                    // ignore data and just dump it
                }
                else
                {
                    // Check if enough data exists to finalize this useful data packet, if not, get another
                    if (indexOfUsefulDataHeader.Value  + LengthOfUsefulPacket
                        > resultArray.Length)
                    {
                        var nextResultsArray = await GetNextBuffer();
                        resultArray = resultArray.Concat(nextResultsArray).ToArray();
                    }

                    var usefulDataPacket =
                        resultArray
                            .Skip(indexOfUsefulDataHeader.Value)
                            .Take(LengthOfUsefulPacket + 4)
                            .ToArray();

                    //Print(string.Join(",", usefulDataPacket.Select(b => b.ToString())));

                    // based on http://wearcam.org/ece516/mindset_communications_protocol.pdf
                    var data = new MindsetData(
                        usefulDataPacket[4],
                        GetIntValue(usefulDataPacket, 7, 9),
                        GetIntValue(usefulDataPacket, 10, 12),
                        GetIntValue(usefulDataPacket, 13, 15),
                        GetIntValue(usefulDataPacket, 16, 18),
                        GetIntValue(usefulDataPacket, 19, 21),
                        GetIntValue(usefulDataPacket, 22, 24),
                        GetIntValue(usefulDataPacket, 25, 27),
                        GetIntValue(usefulDataPacket, 28, 30),
                        usefulDataPacket[32],
                        usefulDataPacket[34],
                        DateTime.Now);
                    Print(data.ToString());
                    HandleMindsetData(data);
                }
            }
        }

        private ObservableCollection<MindsetData> mindsetPoints = new ObservableCollection<MindsetData>(); 
        private void HandleMindsetData(MindsetData data)
        {
            if (data.IsQualityOK)
            {
                if (ConnectionStates.CurrentState != Connected)
                {
                    VisualStateManager.GoToState(this, Connected.Name, true);
                    InitSeries(0, "Delta");
                    InitSeries(1, "Theta");
                    InitSeries(2, "AlphaLow");
                    InitSeries(3, "AlphaHigh");
                    InitSeries(4, "BetaLow");
                    InitSeries(5, "BetaHigh");
                    InitSeries(6, "GammaLow");
                    InitSeries(7, "GammaMid");
                }
                dataPanel.DataContext = data;
                mindsetPoints.Add(data);
            }
            else
            {
                VisualStateManager.GoToState(this, LowQuality.Name, true);
            }
        }

        private void InitSeries(int index, string propertyName)
        {
            chart.Series[index].ItemsSource = mindsetPoints;
            ((LineSeries) chart.Series[index]).ValueBinding = new PropertyNameDataPointBinding(propertyName);
            ((LineSeries) chart.Series[index]).CategoryBinding = new PropertyNameDataPointBinding("Timestamp");
        }

        private int GetIntValue(byte[] usefulDataPacket, int beginInclusive, int endInclusive)
        {
            // http://www.java2s.com/Code/CSharp/Data-Types/ReadInt24frombytearray.htm
            return (usefulDataPacket[beginInclusive] << 16) 
                + (usefulDataPacket[beginInclusive + 1] << 8) 
                + (usefulDataPacket[beginInclusive + 2]);
        }

  

        private const int PARSER_SYNC = 0xAA;
        private const int UsefulDataPacketLength = 32;
        private const int LengthOfUsefulPacket =
            /*Syncs Length*/ 2 +
            /* Packet Legnth */ 1 +
            UsefulDataPacketLength +
            /* checksum */ 1;
        private int? GetUsefulDataHeaderIndex(byte[] resultArray)
        {
            for (int i = 0; i < resultArray.Length - 2; i++)
            {
                if (resultArray[i] == PARSER_SYNC
                    && resultArray[i + 1] == PARSER_SYNC
                    && resultArray[i + 2] == UsefulDataPacketLength)
                {
                    return i;
                }
            }
            return null;
        }

        private async Task<byte[]> GetNextBuffer(uint length = 512)
        {
            var buffer = await socket.InputStream.ReadAsync(new Windows.Storage.Streams.Buffer(length), length,
                                                            InputStreamOptions.None);

            var resultArray = GetBytesFromBuffer(buffer);
            return resultArray;
        }

        private byte GetByteFromBuffer(IBuffer buffer)
        {
            using (var dr = DataReader.FromBuffer(buffer))
            {
                return dr.ReadByte();
            }
        }

        private byte[] GetBytesFromBuffer(IBuffer buffer)
        {
            using (var dr = DataReader.FromBuffer(buffer))
            {
                byte[] bytes = new byte[dr.UnconsumedBufferLength];
                dr.ReadBytes(bytes);
                return bytes;
            }
        }

        private void Print(string str)
        {
            Debug.WriteLine(str);
            //if (lstOutput.Items.Count > 10)
            //{
            //    lstOutput.Items.RemoveAt(0);
            //}

            //var item = str + Environment.NewLine;
            //lstOutput.Items.Add(item);
            //lstOutput.ScrollIntoView(item);

        }

        private void btn_Switch_Click_1(object sender, RoutedEventArgs e)
        {
            if (DisplayGroup.CurrentState == ShowChart)
                VisualStateManager.GoToState(this, ShowData.Name, true);
            else
                VisualStateManager.GoToState(this, ShowChart.Name, true);
        }

        // Sample code for building a localized ApplicationBar
        //private void BuildLocalizedApplicationBar()
        //{
        //    // Set the page's ApplicationBar to a new instance of ApplicationBar.
        //    ApplicationBar = new ApplicationBar();

        //    // Create a new button and set the text value to the localized string from AppResources.
        //    ApplicationBarIconButton appBarButton = new ApplicationBarIconButton(new Uri("/Assets/AppBar/appbar.add.rest.png", UriKind.Relative));
        //    appBarButton.Text = AppResources.AppBarButtonText;
        //    ApplicationBar.Buttons.Add(appBarButton);

        //    // Create a new menu item with the localized string from AppResources.
        //    ApplicationBarMenuItem appBarMenuItem = new ApplicationBarMenuItem(AppResources.AppBarMenuItemText);
        //    ApplicationBar.MenuItems.Add(appBarMenuItem);
        //}
    }

    public class MindsetData
    {
        public MindsetData()
        {
        }

        public bool IsQualityOK { get { return Quality == 0; } }

        public MindsetData(int quality, int delta, int theta, int alphaLow, int alphaHigh, int betaLow, int betaHigh, int gammaLow, int gammaMid, int eSenseAttention, int eSenseMeditation, DateTime timestamp)
        {
            Quality = quality;
            Delta = delta;
            Theta = theta;
            AlphaLow = alphaLow;
            AlphaHigh = alphaHigh;
            BetaLow = betaLow;
            BetaHigh = betaHigh;
            GammaLow = gammaLow;
            GammaMid = gammaMid;
            this.eSenseAttention = eSenseAttention;
            this.eSenseMeditation = eSenseMeditation;
            Timestamp = timestamp;
        }

        /// <summary>
        /// Range 0-200. Lower is better. 0 is no quality issues. 
        /// </summary>
        public int Quality { get; private set; }

        /// <summary>
        /// All ranges are Int24.
        /// </summary>
        public int Delta { get; set; }
        public int Theta { get; private set; }
        public int AlphaLow { get; private set; }
        public int AlphaHigh { get; private set; }
        public int BetaLow { get; private set; }
        public int BetaHigh { get; private set; }
        public int GammaLow { get; private set; }
        public int GammaMid { get; private set; }

        /// <summary>
        /// Range 0-100.
        /// </summary>
        public int eSenseAttention { get; private set; }

        /// <summary>
        /// Range 0-100.
        /// </summary>
        public int eSenseMeditation { get; private set; }

        public override string ToString()
        {
            return string.Format(@"Quality: {0}, Delta: {1}, Theta: {2}, AlphaLow: {3}, AlphaHigh: {4}, BetaLow: {5}, BetaHigh: {6}, GammaLow: {7}, GammaMid: {8}, eSenseAttention: {9}, eSenseMeditation: {10}", Quality, Delta, Theta, AlphaLow, AlphaHigh, BetaLow, BetaHigh, GammaLow, GammaMid, eSenseAttention, eSenseMeditation);
        }

        public DateTime Timestamp { get; private set; }
    }


}